Update CRM Data
What does it do?
Writes structured fields into the current chat's crmData. Use it when you already have values (from prompts, request, transformers, or CRM responses) and need to persist them on the chat so they survive in %chat:crmData.<key>%, are visible to agents, and reachable by later nodes.
For regex-extracting a single field from the user's last message, use parseCrmData instead.
1. Syntax
<node_name>:
type: func
func_type: chat
func_id: updateCrmData
params:
data:
<field_name>: <value>
mergingMode: "<merging_mode>"
on_complete: <next_node>
required params
typetype of the nodefunc_typehere it will be a chat functionfunc_idwhat function are we calling (updateCrmData)params.dataobject of CRM fields to applyon_completenext node after complete
optional params
params.mergingModehowdatais combined with the existingcrmData. Defaults tomerge. One of:replace,assign,merge,defaults,defaultsDeep— see Merging modeson_failurefallback nodedepartmentassigns the chat to a departmentagentassigns the chat to a specific agent (email address or CRM ID as defined in the Texter agents manager)
2. Merging modes
mergingMode controls how data combines with the chat's existing crmData. Default is merge.
| Value | Behavior |
|---|---|
replace | Overwrites crmData with data. Nothing from the previous object is kept. |
assign | Shallow merge — top-level keys from data overwrite existing ones, others stay. Nested objects are replaced, not merged. |
merge | Deep merge — nested objects are merged recursively. New keys are added, existing ones are overwritten. (default) |
defaults | Shallow — only fills top-level keys that are missing on existing crmData. Existing values are never overwritten. |
defaultsDeep | Same as defaults, but applied recursively into nested objects. |
3. Examples
Set CRM fields after collecting them in the flow
persist_profile:
type: func
func_type: chat
func_id: updateCrmData
params:
data:
ticketNumber: "%state:node.ask_ticket.text%"
lastUpdatedBy: "bot"
on_complete: confirm_ticket
Merge API response fields into crmData
save_lookup_to_crm:
type: func
func_type: chat
func_id: updateCrmData
params:
data:
customSegment: "%state:node.external_lookup.response.segment%"
riskScore: "%state:node.external_lookup.response.score%"
on_complete: route_by_segment
replace — wipe and start over
reset_crm_data:
type: func
func_type: chat
func_id: updateCrmData
params:
mergingMode: "replace"
data:
ticketNumber: "12345"
stage: "new"
on_complete: next_step
replaceBefore:
{
"leadSource": "ad",
"customer": { "id": "A1", "name": "Dana" }
}
After:
{
"ticketNumber": "12345",
"stage": "new"
}
Everything that was there before is gone — leadSource and customer are lost.
Reset crmData completely
Use mergingMode: "replace" to wipe crmData. Two forms — both work, with a small difference in the final state:
Option A — omit data (recommended, true reset):
reset_crm_data:
type: func
func_type: chat
func_id: updateCrmData
params:
mergingMode: "replace"
on_complete: start_fresh
Option B — pass an empty object:
reset_crm_data:
type: func
func_type: chat
func_id: updateCrmData
params:
mergingMode: "replace"
data: {}
on_complete: start_fresh
Before:
{
"leadSource": "ad",
"customer": { "id": "A1", "name": "Dana" },
"stage": "qualified"
}
After (Option A — data omitted):
undefined
crmData is removed entirely from the chat.
After (Option B — data: {}):
{}
crmData is kept as an empty object.
In both cases all %chat:crmData.<key>% injections resolve to empty. Prefer Option A for a clean reset; use Option B if downstream code specifically checks for an empty object rather than absence.
assign — shallow overwrite
assign_top_level_fields:
type: func
func_type: chat
func_id: updateCrmData
params:
mergingMode: "assign"
data:
customer:
name: "Dana Cohen"
stage: "qualified"
on_complete: next_step
assignBefore:
{
"leadSource": "ad",
"customer": { "id": "A1", "name": "Dana" },
"stage": "new"
}
After:
{
"leadSource": "ad",
"customer": { "name": "Dana Cohen" },
"stage": "qualified"
}
Note that customer.id is lost — assign replaces the whole customer object instead of merging into it.
merge (default) — deep merge nested objects
deep_merge_customer:
type: func
func_type: chat
func_id: updateCrmData
params:
data:
customer:
name: "Dana Cohen"
email: "dana@example.com"
stage: "qualified"
on_complete: next_step
mergeBefore:
{
"leadSource": "ad",
"customer": { "id": "A1", "name": "Dana" },
"stage": "new"
}
After:
{
"leadSource": "ad",
"customer": { "id": "A1", "name": "Dana Cohen", "email": "dana@example.com" },
"stage": "qualified"
}
customer.id is preserved, customer.name is updated, customer.email is added.
defaults — only fill missing top-level keys
set_defaults_if_missing:
type: func
func_type: chat
func_id: updateCrmData
params:
mergingMode: "defaults"
data:
stage: "new"
leadSource: "organic"
customer:
name: "Unknown"
on_complete: next_step
defaultsBefore:
{
"stage": "qualified",
"customer": { "id": "A1" }
}
After:
{
"stage": "qualified",
"customer": { "id": "A1" },
"leadSource": "organic"
}
stagealready existed → not overwrittenleadSourcedid not exist → addedcustomeralready existed → kept as-is (no deep fill —customer.nameis not added)
defaultsDeep — fill missing keys, deeply
deep_fill_missing:
type: func
func_type: chat
func_id: updateCrmData
params:
mergingMode: "defaultsDeep"
data:
customer:
name: "Unknown"
email: "n/a"
stage: "new"
on_complete: next_step
defaultsDeepBefore:
{
"stage": "qualified",
"customer": { "id": "A1", "name": "Dana" }
}
After:
{
"stage": "qualified",
"customer": { "id": "A1", "name": "Dana", "email": "n/a" }
}
stagealready existed → not overwrittencustomer.namealready existed → not overwrittencustomer.emailwas missing → filled in
For regex extraction from the user's last message into a single crmKey, use parseCrmData. Use updateCrmData when you already have structured values and need to persist them on the chat.
Saved values are available immediately as %chat:crmData.<key>% (including nested paths like %chat:crmData.customer.name%) in subsequent nodes.